iT邦幫忙

2024 iThome 鐵人賽

DAY 24
0
Python

Python 錦囊密技系列 第 24

【Python錦囊㊙️技24】微服務 (Microservices) 【1】-- 行事曆實作

  • 分享至 

  • xImage
  •  

微服務(Microservices)這幾年非常夯,因為企業行之有年之後,內部系統就變成一隻大象,牽一髮動全身,任何功能的新增或修改,都可能造成既有系統的穩定性,例如前一陣子的微軟系統全球大當機,只是一個防毒檔案的更新,就引發全球PC大當機,因此,微服務架構希望能解構傳統的單體式(Monolithic)應用系統架構,提供輕量級(Lightweight)、去中心化(Decentralized)、鬆散耦合(Loosely coupled)的架構,每一個應用系統只負責單一功能(Single Responsibility),獨立開發/運作,不要與核心系統綁在一起。

https://ithelp.ithome.com.tw/upload/images/20241008/20001976B8jqfnmQZR.png
圖一. 微服務(Microservices),圖片來源:【When to Use and When NOT to Use Microservices: No Silver Bullet】

微服務9大最佳實踐(Best practices)

2014年軟體工程大老Martin Fowler與James Lewis共同提出微服務的概念,再經其他專家演繹,共整理出9大最佳實踐(),但是各自表述,並不完全一致,筆者整理裡如下:

  1. 微服務理想的境界是團隊可以獨立開發、測試及佈署應用程式,不會影響既有系統運作(develop, test, and deploy their microservices without impacting others).
  2. 使用輕量的通訊協定:例如REST, gRPC,並以訊息傳遞軟體(Message broker),作為微服務之間的溝通橋樑,知名的訊息傳遞軟體有RabbitMQ、Apache Kafka、Microsoft MSMQ...等,可詳閱【What are Message Brokers in System Design?】
  3. 服務發現(Service discovery):隨著微服務的開發,企業內/外的微服務會越來越多,必須維護一份清單,讓相關人士能找的到及進而使用。
  4. 資料盡量獨立:盡量不要與其他系統儲存在一起,這一點有很大的爭議,因為企業內資料必須共享,若採非常分散的資料庫,整合查詢必然非常困難。比較狹義的規範應該是每一份資料都有所有權人,專人負責管理(Data Ownership)。
  5. 權限控管要統一:避免每一個微服務都要各自登入,最好能做到單一登入(Single sign on, SSO)。
  6. 虛擬容器化(Containerization):由於微服務眾多且量體很小,使用Docker/Kubernetes佈署應用程式,最為方便。
  7. 必須有強韌的網路(resilient network)及機制(Resiliency patterns):因為系統散落在各個電腦或虛擬容器,必須有強韌的網路通訊,否則,會造成大混亂,另外,在失敗時要有補救措施,例如retry機制、caching及使用限制(Rate limit)。
  8. 交易控制(Transaction control):這是微服務最大的挑戰,在分散式的環境下,微服務採用Event store,無法保證資料寫入的完整性,因此,不幸的只有部份交易寫入的情況下,如何補救(Compensation)是非常重要的課題。
  9. 領域驅動設計(Domain-driven design, DDD):微服務的系統分析與設計通常會使用DDD,可參閱筆者的【淺談中台架構、DDD與Python實踐系列文章】

【ByteByteGo EP106】有一張非常美觀的圖,筆者擔心有版權問題,請讀者自行點閱。

實作

以下透過實作,說明微服務的概念,通常微服務會以REST API的形式呈現,Web Server、Mobile或桌面程式呼叫API存取資料,提供REST API開發的python套件有FastAPI、Django REST framework...等,本文以FastAPI為例實作REST API。

範例1. FastAPI簡單測試。

  1. FastAPI安裝:
pip install "fastapi[standard]
  1. 簡單測試:
from fastapi import FastAPI

# 建立 FastAPI 物件
app = FastAPI()

# 設定路由及其處理函數
@app.get("/")
async def root():
    return {"message": "Hello World"}
  1. 執行:
fastapi dev fastapi1.py
  1. 開啟瀏覽器,輸入:
http://localhost:8000/
  1. 執行結果:
{"message":"Hello World"}

範例2. 行事曆實作,Server端以FastAPI為例實作REST API,Client端借用【JavaScript Event Calendar for Resource Scheduling】一文的JavaScript元件。

  1. 【JavaScript Event Calendar for Resource Scheduling】下載【DayPilot Lite for JavaScript 2024.3 Event Calendar】,並解壓縮。

  2. 複製demo資料夾內的子目錄至24\static資料夾,index.html則複製至24\templates資料夾。
    https://ithelp.ithome.com.tw/upload/images/20241008/20001976mwYSdTzTV7.png

  3. 修改css、js路徑以符合Jinja2格式如下,完整內容請參考24\templates\index.html。

<script src="{{ url_for('static', path='/js/daypilot-all.min.js?v=2024.3.547') }}"></script>
<link type="text/css" rel="stylesheet" href="{{ url_for('static', path='/themes/calendar_green.css') }}"/>
  1. 建立SQLite資料庫calendar.db:只有一個資料表,SQL如下。
CREATE TABLE "calendar" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "text" varchar(120) NOT NULL, "start" datetime NOT NULL, "end" datetime NOT NULL);
  1. 撰寫FastAPI程式:依據index.html內定的API URL,撰寫server端API。針對FastAPI進行初始化及定義執行SQL的函數,程式名稱為fastapi_calendar.py。
from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
import sqlite3
from dataclasses import dataclass
from datetime import datetime

app = FastAPI(debug=True, redirect_slashes=False)
app.mount("/static", StaticFiles(directory="static"), name="static")
templates = Jinja2Templates(directory="templates")

@dataclass
class Event():
    # id: int
    text: str
    start:datetime
    end:datetime 
    
def execute_SQL(sql):
    con = sqlite3.connect("calendar.db")
    cur = con.cursor()
    result = cur.execute(sql)
    con.commit() 
    con.close()
    return result

def execute_select_SQL(sql):
    con = sqlite3.connect("calendar.db")
    cur = con.cursor()
    result = cur.execute(sql)
    data = result.fetchall()
    con.commit() 
    con.close()
    return data
  1. 首頁:
@app.get("/", response_class=HTMLResponse)
async def index(request: Request):
    return templates.TemplateResponse(
        "index.html", {"request": request}
    )
  1. 取得一段時間的行事曆:使用get。
@app.get("/api/CalendarEvents")
async def get_events(start, end):
    sql = f"select * from calendar where start >= '{start}' and end <= '{end}'"
    response = []
    result = execute_select_SQL(sql)
    for row in result:
        dict1 = {}
        dict1['id'] = row[0]
        dict1['text'] = row[1]
        dict1['start'] = row[2]
        dict1['end'] = row[3]
        response.append(dict1)
    return response 
  1. 新增行事曆:post。
@app.post("/api/CalendarEvents")
async def add_events(event:Event):
    # SSprint(event.start, event.end, event.text)
    sql = f"insert into calendar (text, start, end) values ('{event.text}', '{event.start}', '{event.end}')"
    result = execute_select_SQL(sql)
    dict1 = {}
    dict1['id'] = result
    dict1['text'] = event.text
    dict1['start'] = event.start
    dict1['end'] = event.end
    return dict1
  1. 修改行事曆:put。
@app.put("/api/CalendarEvents/{id}")
async def update_events(id, event:Event):
    # print(event.start, event.end, event.text)
    sql = f"update calendar set text='{event.text}', start='{event.start}', end='{event.end}' "
    sql += f" where id={id}"
    result = execute_SQL(sql)
    return {}
  1. 刪除行事曆:delete。
@app.delete("/api/CalendarEvents/{id}")
async def delete_events(id):
    sql = f"delete from calendar where id = {id}"
    result = execute_SQL(sql)
    return {}    
  1. 執行:
fastapi dev fastapi_calendar.py
  1. 開啟瀏覽器,輸入:
http://localhost:8000/
  1. 執行結果:
    https://ithelp.ithome.com.tw/upload/images/20241008/20001976mIcKRrzRqY.png

  2. 行事曆可新增(以滑鼠拖曳一段時間)、刪除、更正、複製、移動(以滑鼠拖曳)及改變起訖時間(以滑鼠拖曳)。

補充說明:

  1. 完全以REST API開發,使用HTTP方法(get、post、put、delete)及URL附帶參數。
  2. FastAPI以Data class接收JSON資料,並可檢查資料型別及驗證。
  3. 此範例因JavaScript元件的URL採相對路徑,故將FastAPI程式與index.html合為一專案,正常應分為兩個網站。
  4. 此範例不只可應用於行事曆,也可以應用於會議室、公務車預定...等。

結語

微服務(Microservices)適合較單純且獨立的應用程式開發,最好是非關鍵性的專案,例如【WebEIP Pro】所列項目。
https://ithelp.ithome.com.tw/upload/images/20241008/20001976xIiJsGFxin.jpg
圖二. 適合微服務的專案,圖片來源:【WebEIP PRO企業團隊管理解決方案】

本系列的程式碼會統一放在GitHub,本篇的程式放在src/24資料夾,歡迎讀者下載測試,如有錯誤或疏漏,請不吝指正。


上一篇
【Python錦囊㊙️技23】Python/Django與Redis 整合
下一篇
【Python錦囊㊙️技25】微服務 (Microservices) 【2】-- 容器化佈署
系列文
Python 錦囊密技30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言